/*
 * Copyright 2023-2024, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Haiku Development Team
 */

#include <asm_defs.h>
#include <arch/cpu.h>
#include <arch/thread.h>
#include <arch_cpu_defs.h>
#include <arch_thread_types.h>
#include <syscall_numbers.h>

// 定义CSR寄存器地址
#define CSR_CRMD      0x0     // 当前模式信息
#define CSR_PRMD      0x1     // 上一次模式信息
#define CSR_EUEN      0x2     // 扩展单元使能
#define CSR_ECFG      0x4     // 异常配置
#define CSR_ESTAT     0x5     // 异常状态
#define CSR_ERA       0x6     // 异常返回地址
#define CSR_BADV      0x7     // 异常访问地址
#define CSR_EENTRY    0xc     // 异常入口地址

// 定义CRMD寄存器位
#define CRMD_PLV_MASK 0x3     // 特权级掩码
#define CRMD_PLV_KERN 0x0     // 内核特权级
#define CRMD_PLV_USER 0x3     // 用户特权级
#define CRMD_IE       0x4     // 中断使能位

// 定义栈帧大小
#define IFRAME_SIZE   (41 * 8) // iframe结构体大小

// 保存通用寄存器到栈上
.macro SAVE_REGS
	addi.d  $sp, $sp, -IFRAME_SIZE
	st.d    $zero, $sp, 0
	st.d    $ra, $sp, 8
	st.d    $tp, $sp, 16
	// $sp已经被修改，不需要保存
	st.d    $a0, $sp, 32
	st.d    $a1, $sp, 40
	st.d    $a2, $sp, 48
	st.d    $a3, $sp, 56
	st.d    $a4, $sp, 64
	st.d    $a5, $sp, 72
	st.d    $a6, $sp, 80
	st.d    $a7, $sp, 88
	st.d    $t0, $sp, 96
	st.d    $t1, $sp, 104
	st.d    $t2, $sp, 112
	st.d    $t3, $sp, 120
	st.d    $t4, $sp, 128
	st.d    $t5, $sp, 136
	st.d    $t6, $sp, 144
	st.d    $t7, $sp, 152
	st.d    $t8, $sp, 160
	st.d    $r21, $sp, 168
	st.d    $fp, $sp, 176
	st.d    $s0, $sp, 184
	st.d    $s1, $sp, 192
	st.d    $s2, $sp, 200
	st.d    $s3, $sp, 208
	st.d    $s4, $sp, 216
	st.d    $s5, $sp, 224
	st.d    $s6, $sp, 232
	st.d    $s7, $sp, 240
	st.d    $s8, $sp, 248

	// 保存特殊寄存器
	csrrd   $t0, CSR_ERA
	st.d    $t0, $sp, 256
	csrrd   $t0, CSR_BADV
	st.d    $t0, $sp, 264
	csrrd   $t0, CSR_CRMD
	st.d    $t0, $sp, 272
	csrrd   $t0, CSR_PRMD
	st.d    $t0, $sp, 280
	csrrd   $t0, CSR_EUEN
	st.d    $t0, $sp, 288
	csrrd   $t0, CSR_ECFG
	st.d    $t0, $sp, 296
	csrrd   $t0, CSR_ESTAT
	st.d    $t0, $sp, 304
.endm

// 从栈上恢复通用寄存器
.macro RESTORE_REGS
	ld.d    $zero, $sp, 0
	ld.d    $ra, $sp, 8
	ld.d    $tp, $sp, 16
	// $sp将在最后恢复
	ld.d    $a0, $sp, 32
	ld.d    $a1, $sp, 40
	ld.d    $a2, $sp, 48
	ld.d    $a3, $sp, 56
	ld.d    $a4, $sp, 64
	ld.d    $a5, $sp, 72
	ld.d    $a6, $sp, 80
	ld.d    $a7, $sp, 88
	ld.d    $t0, $sp, 96
	ld.d    $t1, $sp, 104
	ld.d    $t2, $sp, 112
	ld.d    $t3, $sp, 120
	ld.d    $t4, $sp, 128
	ld.d    $t5, $sp, 136
	ld.d    $t6, $sp, 144
	ld.d    $t7, $sp, 152
	ld.d    $t8, $sp, 160
	ld.d    $r21, $sp, 168
	ld.d    $fp, $sp, 176
	ld.d    $s0, $sp, 184
	ld.d    $s1, $sp, 192
	ld.d    $s2, $sp, 200
	ld.d    $s3, $sp, 208
	ld.d    $s4, $sp, 216
	ld.d    $s5, $sp, 224
	ld.d    $s6, $sp, 232
	ld.d    $s7, $sp, 240
	ld.d    $s8, $sp, 248

	// 恢复特殊寄存器
	ld.d    $t0, $sp, 256
	csrwr   $t0, CSR_ERA
	ld.d    $t0, $sp, 272
	csrwr   $t0, CSR_CRMD
	ld.d    $t0, $sp, 280
	csrwr   $t0, CSR_PRMD
	ld.d    $t0, $sp, 288
	csrwr   $t0, CSR_EUEN

	addi.d  $sp, $sp, IFRAME_SIZE
.endm

// 异常处理入口点
FUNCTION(loongarch64_exception_vector):
	SAVE_REGS

	// 调用C语言异常处理函数
	move    $a0, $sp    // 传递iframe指针作为参数
	bl      loongarch64_exception_entry

	RESTORE_REGS
	ertn                // 返回到异常发生点
FUNCTION_END(loongarch64_exception_vector)

// 线程上下文切换函数
// void switch_threads(Thread* from, Thread* to);
FUNCTION(switch_threads):
	// 保存当前线程上下文
	st.d    $ra, $a0, 8     // 保存返回地址
	st.d    $sp, $a0, 16    // 保存栈指针
	st.d    $fp, $a0, 176   // 保存帧指针
	st.d    $s0, $a0, 184   // 保存s0-s8寄存器
	st.d    $s1, $a0, 192
	st.d    $s2, $a0, 200
	st.d    $s3, $a0, 208
	st.d    $s4, $a0, 216
	st.d    $s5, $a0, 224
	st.d    $s6, $a0, 232
	st.d    $s7, $a0, 240
	st.d    $s8, $a0, 248

	// 加载目标线程上下文
	ld.d    $ra, $a1, 8     // 加载返回地址
	ld.d    $sp, $a1, 16    // 加载栈指针
	ld.d    $fp, $a1, 176   // 加载帧指针
	ld.d    $s0, $a1, 184   // 加载s0-s8寄存器
	ld.d    $s1, $a1, 192
	ld.d    $s2, $a1, 200
	ld.d    $s3, $a1, 208
	ld.d    $s4, $a1, 216
	ld.d    $s5, $a1, 224
	ld.d    $s6, $a1, 232
	ld.d    $s7, $a1, 240
	ld.d    $s8, $a1, 248

	ret                     // 返回到新线程
FUNCTION_END(switch_threads)

// 进入用户空间函数
// void arch_enter_userspace(iframe* frame);
FUNCTION(arch_enter_userspace):
	// 设置用户模式
	csrrd   $t0, CSR_CRMD
	andi    $t0, $t0, ~CRMD_PLV_MASK
	ori     $t0, $t0, CRMD_PLV_USER
	csrwr   $t0, CSR_CRMD

	// 加载用户空间iframe
	RESTORE_REGS

	// 返回到用户空间
	ertn
FUNCTION_END(arch_enter_userspace)

// 系统调用处理入口点
FUNCTION(loongarch64_syscall_vector):
	SAVE_REGS

	// 调用C语言系统调用处理函数
	move    $a0, $sp    // 传递iframe指针作为参数
	bl      loongarch64_syscall_entry

	RESTORE_REGS
	ertn                // 返回到用户空间
FUNCTION_END(loongarch64_syscall_vector)